package cn.tedu.csmall.sso.filter;


import cn.tedu.csmall.commons.utils.JwtUtils;
import cn.tedu.csmall.sso.utils.LoginPrincipal;
import com.alibaba.fastjson.JSON;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * 自定义过滤器
 * 用来解析jwt信息
 * 此过滤器将尝试获取请求中的JWT数据，如果存在有效数据，将尝试解析
 * 然后，将解析得到的结果存入到Spring Security的上下文中，
 * 以至于Spring Security框架中的其它组件能够从上下文中获取到用户的信息，
 *从而完成后续的授权访问
 */
@Component
@Slf4j
public class JwtAuthorizationFilter extends OncePerRequestFilter {
    // 最终，过滤器可以选择“阻止”或“放行”
    // 如果选择“阻止”，则后续的所有组件都不会被执行
    // 如果选择“放行”，会执行“过滤器链”中剩余的部分，甚至继续向后执行到控制器等组件
    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest,
                                    HttpServletResponse httpServletResponse,
                                    FilterChain filterChain)
            throws ServletException, IOException {
        //清缓存  清除安全框架上下文的缓存
        SecurityContextHolder.clearContext();
        //放行请求  放行的login请求
        String[] permitUrls = {"/admins/login"};
        String requestURI = httpServletRequest.getRequestURI();
        if (Arrays.asList(permitUrls).contains(requestURI)){
            log.info("包含可放行的请求路径");
            filterChain.doFilter(httpServletRequest,httpServletResponse);
            //放行之后就不需要往下做  结束过滤器代码
            return;
        }
        // 从请求中获取jwt---请求头内部---自定义  Authorization=”jwt“
        String jwt = httpServletRequest.getHeader("Authorization");
        log.info("从请求头中获取jwt:{}",jwt);
        //判断是否 有 有效的jwt数据  true ---有效的   false无效的
        //如果 jwt无效 没有 【需要放行的请求 比如说 接口文档等】 放行
        if (!StringUtils.hasText(jwt)){
            log.info("请求头中的jwt无效，选择直接放行");
            filterChain.doFilter(httpServletRequest,httpServletResponse);
            //放行之后就不需要往下做  结束过滤器代码
            return;
        }
        //如果有 就解析
        Claims claims = JwtUtils.parse(jwt);
        Object id = claims.get("id");
        log.info("id:{}",id);
        Object username = claims.get("username");
        log.info("解析的username:{}",username);
        Object authoritiesJson = claims.get("authorities");
        log.info("authoritiesJson:{}",authoritiesJson);
        //权限
        //把权限从JSON字符串格式转成List对象
        List<SimpleGrantedAuthority> authorities
                = JSON.parseArray(
                        authoritiesJson.toString(),
                        SimpleGrantedAuthority.class);

        //把用户名和id 封装到 LoginPrincipal
        LoginPrincipal loginPrincipal = new LoginPrincipal();
        loginPrincipal.setId(Long.parseLong(id.toString()));
        loginPrincipal.setUsername(username.toString());
        //解析之后 把有效数据 放到安全框架的上下文中
        // 安全框架中存储的数据 包含 username  密码【null】 权限数据【模拟一个临时数据】
        Authentication authentication
                = new UsernamePasswordAuthenticationToken(loginPrincipal,
                null, authorities);

        SecurityContext context = SecurityContextHolder.getContext();
        context.setAuthentication(authentication);
        //放行
        filterChain.doFilter(httpServletRequest,httpServletResponse);
    }
}
